﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Liang.Net
{
    public class TCPServer : IDisposable
    {
        TcpListener m_Listener = null;
        List<TCPClient> m_ClientManager = new List<TCPClient>();

        /// <summary>
        /// 连接同步信号
        /// </summary>
        ManualResetEvent tcpClientConnected = new ManualResetEvent(false);

        ReceiveDataDelegate m_ReceiveDataHandler = null;

        public TCPServer(int nPort, ReceiveDataDelegate receiveDataHandler)
        {
            if (null == receiveDataHandler)
            {
                throw new ArgumentNullException("数据处理句柄不能为空");
            }

            m_Listener = new TcpListener(IPAddress.Any, nPort);
            m_ReceiveDataHandler = receiveDataHandler;

            // 启动监听
            m_Listener.Start();

            // 启动接收连接循环
            Task.Factory.StartNew(ListenLoop);
        }

        /// <summary>
        /// 监听循环
        /// <para>.net 4.5 之后用 async void ListenLoop()</para>
        /// </summary>
        void ListenLoop()
        {
            while (true)
            {
                try
                {
                    #region .net 4.0 异步接收方式

                    // 重置同步对象为无信号状态
                    tcpClientConnected.Reset();

                    // 提交一个接收连接尝试
                    // 该函数不会导致线程阻塞
                    // 所以在调用该函数后，需要等待当次连接尝试操作完成，才能继续下一个接收连接的尝试
                    // 因此，在当次连接完成之前，不会有新的连接尝试被提交（或创建）
                    IAsyncResult ar = 
                    m_Listener.BeginAcceptTcpClient(
                        new AsyncCallback(DoAcceptTcpClientCallback), 
                        m_Listener);

                    if (null == ar)
                    {
                        break;
                    }

                    // 等待一个连接完成
                    // 该同步对象变为有信号是在 DoAcceptTcpClientCallback 中完成的
                    tcpClientConnected.WaitOne();

                    #endregion

                    #region .net 4.5 异步接收方式
                    //var tcpClient = await m_Listener.AcceptTcpClientAsync();

                    //if (null == tcpClient)
                    //{
                    //    break;
                    //}

                    //// 接受连接后，新建的client，在构造函数里，会开启接收数据事件，直至关闭或异常退出
                    //var client = new TCPClient(tcpClient, m_ReceiveDataHandler, ClientDisconnect);
                    //m_ClientManager.Add(client);
                    //byte[] echo = Encoding.ASCII.GetBytes("connected.");
                    //client.Send(echo);
                    #endregion
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }

        /// <summary>
        /// 处理异步方式下的客户连接
        /// </summary>
        /// <param name="ar"></param>
        public void DoAcceptTcpClientCallback(IAsyncResult ar)
        {
            TcpListener listener = (TcpListener)ar.AsyncState;
            TcpClient tcpClient = listener.EndAcceptTcpClient(ar);

            // 将接入的客户端纳入管理
            var client = new TCPClient(tcpClient, m_ReceiveDataHandler, ClientDisconnect);
            m_ClientManager.Add(client);

            // 使同步对象变为有信号
            tcpClientConnected.Set();
        }

        void ClientDisconnect(TCPClient client)
        {
            try
            {
                if (null != client)
                {
                    client.Dispose();
                    m_ClientManager.Remove(client);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public void Dispose()
        {
            if (null != m_Listener)
            {
                m_Listener.Stop();
            }
            foreach (var client in m_ClientManager)
            {
                client.Dispose();
            }
        }
    }
}
